package com.lili.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lili.base.constants.*;
import com.lili.base.vo.ResultAPI;
import com.lili.config.cache.RedisCache;
import com.lili.config.mvc.security.vo.LoginUser;
import com.lili.config.sysLog.UserEvent;
import com.lili.exceptions.CustomException;
import com.lili.mapper.UserMapper;
import com.lili.pojo.*;
import com.lili.service.*;
import com.lili.utils.*;
import com.lili.vo.request.*;
import com.lili.vo.request.admin.UserReqVO;
import com.lili.vo.request.other.UpdatePassReqVO;
import com.lili.vo.request.troop.ResetPassReqVO;
import io.jsonwebtoken.Claims;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.*;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements UserService {

    private final AuthenticationManager authenticationManager;
    private final RedisCache redisCache;
    private final PasswordEncoder passwordEncoder;
    private final EmailService emailService;
    private final FileUpLoadServiceImpl fileUpLoadService;

    private final ApplicationEventPublisher publisher;

    @Value("${img.basePath}")
    private String basePath;

    @Autowired
    public UserServiceImpl(AuthenticationManager authenticationManager, RedisCache redisCache, PasswordEncoder passwordEncoder,
                           ApplicationEventPublisher publisher, EmailService emailService, FileUpLoadServiceImpl fileUpLoadService) {
        this.authenticationManager = authenticationManager;
        this.redisCache = redisCache;
        this.passwordEncoder = passwordEncoder;
        this.publisher = publisher;
        this.emailService = emailService;
        this.fileUpLoadService = fileUpLoadService;
    }

    @Override
    public IPage<User> findUserByCondition(UserReqVO vo) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(!Strings.isEmpty(vo.getUsername()), User::getUsername, vo.getUsername())
                .eq(User::getRole, vo.getRole())
                .eq(vo.getStatus() != null, User::getUserStatus, vo.getStatus())
                .eq(User::getDeleted, 0)
                .orderByDesc(User::getCreateTime);
        IPage<User> page = new Page<>(vo.getPageIndex(), vo.getPageSize());
        IPage<User> pages = baseMapper.selectPage(page, wrapper);

        if (pages.getPages() < vo.getPageIndex()) {
            vo.setPageIndex(1L);
            page = new Page<>(vo.getPageIndex(), vo.getPageSize());
            pages = baseMapper.selectPage(page, wrapper);
        }

        return pages;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultAPI<String> editUser(User user) {
        String msg = "";
        int count = 0;
        if (user.getId() == null) {
            String account = user.getAccount();
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(User::getDeleted, 0).eq(User::getAccount, account);
            List<User> users = this.getBaseMapper().selectList(wrapper);
            if (users.size() > 0) {
                count = 0;
                msg = "已有该账号存在的用户";
            } else {
                String secretPass = passwordEncoder.encode(user.getPassword());
                user.setPassword(secretPass);
                user.setImagePath("52bb3462f22e450d9cae1e1a265e1902.png");
                count = baseMapper.insert(user);
                msg = count > 0 ? "添加成功" : "添加失败";
                count = count > 0 ? 200 : 0;
            }
        } else {
            String account = user.getAccount();
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(User::getDeleted, 0).eq(User::getAccount, account).notIn(User::getId, user.getId());
            List<User> users = this.getBaseMapper().selectList(wrapper);
            if (users.size() > 0) {
                count = 0;
                msg = "已有该账号存在的用户";
            } else {
                User userById = this.getBaseMapper().selectById(user.getId());
                if (!userById.getPassword().equals(user.getPassword())) {
                    user.setPassword(passwordEncoder.encode(user.getPassword()));
                }
                user.setImagePath(userById.getImagePath());
                redisCache.delCacheMapValue(RedisConstant.USER, String.valueOf(user.getId()));
                redisCache.deleteObject("edu-login:" + userById.getAccount());
                count = baseMapper.updateById(user);
                msg = count > 0 ? "更新成功" : "更新失败";
                count = count > 0 ? 200 : 0;
            }
        }
        return new ResultAPI<>(count, msg, null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultAPI<String> updateStatus(Integer[] ids) {
        User user = this.getById(ids[0]);
        String msg = user.getUserStatus() == 0 ? "禁用成功" : "启用成功";
        user.setUserStatus(user.getUserStatus() == 0 ? 1 : 0);
        boolean result = this.updateById(user);
        if (result) {
            redisCache.delCacheMapValue(RedisConstant.USER, String.valueOf(user.getId()));
            redisCache.deleteObject("edu-login:" + user.getAccount());
            return new ResultAPI<>(200, msg, null);
        } else {
            return new ResultAPI<>(0, "操作失败", null);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, String> login(LoginUserReq user) {
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getAccount(), user.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        User one = loginUser.getUser();
        if (!one.getRole().equals(user.getRole())) {
            throw new CustomException("权限不足");
        }
        String account = one.getAccount();
        String token = JwtUtil.createJWT(account);
        Map<String, String> map = new HashMap<>();
        map.put(CommonConstant.TOKEN, token);
        redisCache.setCacheObject(RedisConstant.LOGIN_PREFIX + account, loginUser, 180, TimeUnit.MINUTES);
        Log log = new Log(one.getUsername() + "登录了益军考训系统", one.getUsername(), one.getId(), 1, new Date());
        publisher.publishEvent(new UserEvent(log));
        return map;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteUser(Integer id) {
        String account = this.getById(id).getAccount();
        boolean flag = baseMapper.removeById(id) > 0;
        redisCache.delCacheMapValue(RedisConstant.USER, String.valueOf(id));
        redisCache.deleteObject("edu-login:" + account);
        return flag;
    }


    @Override
    public List<User> getUserByUserName(String username) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper
                .eq(User::getRole, 2)
                .eq(User::getDeleted, false)
                .like(Strings.isNotEmpty(username), User::getUsername, username);
        return baseMapper.selectList(wrapper);
    }

    @Override
    @Transactional
    public String updateAvator(MultipartFile file, Integer userId, HttpServletRequest request) {
        try {
            String path = fileUpLoadService.uploadFile(file);
            String url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/avatar/" + path;
            User user = baseMapper.selectById(userId);
            String imagePath = user.getImagePath() == null ? "" : user.getImagePath();
            imagePath = basePath + imagePath;
            File f = new File(imagePath);
            if (f.exists()) {
                f.delete();
            }
            user.setImagePath(path);
            baseMapper.updateById(user);
            System.out.println(user);
            // 更新缓存
            Object obj = redisCache.getCacheMapValue(RedisConstant.USER, String.valueOf(userId));
            if (!Objects.isNull(obj)) {
                User redisUser = JSON.parseObject(JSON.toJSONString(obj), User.class);
                redisUser.setImagePath(url);
                redisCache.setCacheMapValue(RedisConstant.USER, String.valueOf(userId), redisUser);
            }
            return url;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "false";

    }

    /**
     * 更新个人信息
     *
     * @param user
     */
    @Override
    public void updateUser(User user) {
        baseMapper.updateUser(user);
        redisCache.delCacheMapValue(RedisConstant.USER, String.valueOf(user.getId()));
        String redisKey = RedisConstant.LOGIN_PREFIX+user.getAccount();
        Object obj = redisCache.getCacheObject(redisKey);
        LoginUser loginUser = JSON.parseObject(JSON.toJSONString(obj),LoginUser.class);
        loginUser.setUser(user);
        redisCache.setCacheObject(redisKey,loginUser,180,TimeUnit.MINUTES);
    }

    /**
     * @param vo
     * @param userId
     * @return
     */
    @Override
    @Transactional
    public boolean updatePass(UpdatePassReqVO vo, Integer userId) {
        User user = baseMapper.selectById(userId);
        if (!passwordEncoder.matches(vo.getOldPass(), user.getPassword())) {
            return false;
        }
        String pass = passwordEncoder.encode(vo.getNewPass());
        user.setPassword(pass);
        boolean flag = baseMapper.updateById(user) > 0;
        //进行redis的数据更新
        Object obj = redisCache.getCacheMapValue(RedisConstant.USER, String.valueOf(userId));
        User redisUser = JSON.parseObject(JSON.toJSONString(obj), User.class);
        redisUser.setPassword(pass);
        String redisKey = RedisConstant.LOGIN_PREFIX+user.getAccount();
        Object obj1 = redisCache.getCacheObject(redisKey);
        LoginUser login = JSON.parseObject(JSON.toJSONString(obj1),LoginUser.class);
        login.getUser().setPassword(pass);
        redisCache.setCacheObject(redisKey,login,180,TimeUnit.MINUTES);
        return flag;
    }

    @Override
    public void logout(String token) {
        Claims claims = null;
        try {
            claims = JwtUtil.parseJWT(token);
        } catch (Exception e) {
            e.printStackTrace();
        }
        String account = claims.getSubject();
        String redisKey = RedisConstant.LOGIN_PREFIX + account;
        boolean flag = redisCache.deleteObject(redisKey);
    }

    @Override
    public Boolean resetPass(ResetPassReqVO vo) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getAccount, vo.getAccount())
                .eq(User::getUserEmail, vo.getUserEmail())
                .eq(User::getRole, 2);
        User user = baseMapper.selectOne(wrapper);
        if (Objects.isNull(user)) {
            return false;
        }
        // 验证码过期
        String code = redisCache.getCacheObject(user.getUserEmail());
        if (Strings.isEmpty(code)) {
            return false;
        }
        // 验证成功
        if (code.equals(vo.getVailCode())) {
            String pass = Utils.resetPass();
            String secretPass = passwordEncoder.encode(pass);
            user.setPassword(secretPass);
            baseMapper.updateById(user);
            // 异步发送信息，避免响应速度慢
            new Thread(() -> {
                emailService.sendRestPass(vo.getUserEmail(), pass);
            }).start();
            return true;
        }
        return false;
    }

    @Override
    public User getUserById(Integer id) {
        User user = null;
//        Object obj = redisCache.getCacheObject("user:" + id);
        Object obj = redisCache.getCacheMapValue(RedisConstant.USER, String.valueOf(id));
        if (!Objects.isNull(obj)) {
            user = (User) obj;
        } else {
            user = baseMapper.selectById(id);
            redisCache.setCacheMapValue(RedisConstant.USER, String.valueOf(id), user);
        }
        return user;
    }

    @Override
    @Transactional
    public String register(RegisterUserReq req) {
        // 后端检测账号是否已经被注册
        if (!Objects.isNull(getOne(new LambdaQueryWrapper<User>().eq(User::getAccount, req.getAccount()).eq(User::getDeleted, 0)))) {
            return "账号已被注册，请另换";
        }
        User registerUser = new User();
        BeanUtils.copyProperties(req, registerUser);
        registerUser.setPassword(passwordEncoder.encode(req.getPassword()));
        registerUser.setImagePath("52bb3462f22e450d9cae1e1a265e1902.png");
        return save(registerUser) ? "true" : "注册失败，请稍后重试";
    }

    @Override
    public User getCurrent(Integer userId, HttpServletRequest request) {
        Object obj = redisCache.getCacheMapValue(RedisConstant.USER, String.valueOf(userId));
        User user = null;
        if (Objects.isNull(obj)) {
            user = baseMapper.selectById(userId);
            user.setImagePath(request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/avatar/" + user.getImagePath());
            redisCache.setCacheMapValue(RedisConstant.USER, String.valueOf(userId), user);
        } else {
            user = JSON.parseObject(JSON.toJSONString(obj), User.class);
        }
        return user;
    }

    @Override
    public User getInfo(String token, HttpServletRequest request) {
        String account = "";
        String key = "";
        try {
            Claims claims = JwtUtil.parseJWT(token);
            account = claims.getSubject();
            key = RedisConstant.LOGIN_PREFIX + account;
        } catch (Exception e) {
            e.printStackTrace();
        }
        LoginUser loginUser = JSON.parseObject(JSON.toJSONString(redisCache.getCacheObject(key)), LoginUser.class);
        Integer userId = loginUser.getUser().getId();
        Object obj = redisCache.getCacheMapValue(RedisConstant.USER, String.valueOf(userId));
        User user = JSON.parseObject(JSON.toJSONString(obj), User.class);
        if (Objects.isNull(obj)) {
            user = baseMapper.selectById(userId);
            user.setImagePath(request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/avatar/" + user.getImagePath());
            redisCache.setCacheMapValue(RedisConstant.USER, String.valueOf(userId), user);
        }
        return user;
    }


}
